package com.cc.jcmplayer;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * This class includes utility functions for Android permissions related operations.
 */
public final class PermissionsUtil {
    private static final String TAG = PermissionsUtil.class.getSimpleName();

    public static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
    public static final int REQUEST_CAMERA_PERMISSION = 2;
    public static final int REQUEST_VIBRATE_PERMISSION = 3;
    public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
    public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
    public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;

    private PermissionsUtil() {
    }

    /**
     * Request a list of dangerous permissions. The requested permissions must be included in the app's AndroidManifest
     * @param permissions list of the permissions to request.
     * @param activity the caller activity for this method.
     * @return true/false. "true" if permissions are already granted, "false" if a permissions request was dispatched.
     */
    public static boolean requestPermissions(Activity activity, List<String> permissions) {
        if (activity == null) {
            return false;
        }

        if (permissions == null || permissions.isEmpty()) {
            return true;
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            // Not necessary, asked on install already
            return true;
        }


        Set<String> requestedPermissions = new HashSet<>();
        for (String permission : permissions) {
            try {
                if (permission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
                        Log.d(TAG, "Requesting permission " + permission);
                        try {
                            Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                            intent.setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
                            activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE);
                        } catch (Exception ignored) {
                            Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
                            activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE);
                        }
                    }
                } else {
                    PermissionInfo permissionInfo = getPermissionInfo(activity, permission);
                    int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
                    if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "Requesting permission " + permission);
                        requestedPermissions.add(permission);
                    }
                }
            } catch (PackageManager.NameNotFoundException e) {
                // Skip this permission and continue.
                Log.w(TAG, "Unable to identify permission " + permission, e);
            }
        }

        if (requestedPermissions.isEmpty()) {
            // If list is empty, all of dangerous permissions were granted.
            return true;
        }

        activity.requestPermissions(requestedPermissions.toArray(new String[0]), REQUEST_ALL_PERMISSION_REQ_CODE);
        return true;
    }

    /**
     * Request a dangerous permission. The requested permission must be included in the app's AndroidManifest
     * @param permissionName the name of the permission to request.
     * @param activity the caller activity for this method.
     * @return true/false. "true" if permission is already granted, "false" if a permission request was dispatched.
     */
    public static boolean requestPermission(String permissionName, Activity activity) {
        if (activity == null || TextUtils.isEmpty(permissionName)) {
            return false;
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            // Not necessary, asked on install already
            return true;
        }

        switch (permissionName) {
            case "RECORD_AUDIO":
            case Manifest.permission.RECORD_AUDIO:
                if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
                    activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
                    return false;
                }
                return true;

            case "CAMERA":
            case Manifest.permission.CAMERA:
                if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
                    return false;
                }
                return true;

            case "VIBRATE":
            case Manifest.permission.VIBRATE:
                if (ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
                    activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
                    return false;
                }
                return true;

            default:
                // Check if the given permission is a dangerous permission
                try {
                    PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName);
                    int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
                    if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) {
                        activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE);
                        return false;
                    }
                } catch (PackageManager.NameNotFoundException e) {
                    // Unknown permission - return false as it can't be granted.
                    Log.w(TAG, "Unable to identify permission " + permissionName, e);
                    return false;
                }
                return true;
        }
    }

    /**
     * Request dangerous permissions which are defined in the Android manifest file from the user.
     * @param activity the caller activity for this method.
     * @return true/false. "true" if all permissions were already granted, returns "false" if permissions requests were dispatched.
     */
    public static boolean requestManifestPermissions(Activity activity) {
        return requestManifestPermissions(activity, null);
    }

    /**
     * Request dangerous permissions which are defined in the Android manifest file from the user.
     * @param activity the caller activity for this method.
     * @param excludes Set of permissions to exclude from the request
     * @return true/false. "true" if all permissions were already granted, returns "false" if permissions requests were dispatched.
     */
    public static boolean requestManifestPermissions(Activity activity, @Nullable Set<String> excludes) {
        if (activity == null) {
            return false;
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }

        List<String> manifestPermissions;
        try {
            manifestPermissions = getManifestPermissions(activity);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return false;
        }

        if (manifestPermissions.isEmpty()) {
            return true;
        }

        if (excludes != null && !excludes.isEmpty()) {
            for (String excludedPermission : excludes) {
                manifestPermissions.remove(excludedPermission);
            }
        }

        return requestPermissions(activity, manifestPermissions);
    }

    /**
     * With this function you can get the list of dangerous permissions that have been granted to the Android application.
     * @param context the caller context for this method.
     * @return granted permissions list
     */
    public static String[] getGrantedPermissions(Context context) {
        List<String> manifestPermissions;
        try {
            manifestPermissions = getManifestPermissions(context);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return new String[0];
        }
        if (manifestPermissions.isEmpty()) {
            return new String[0];
        }

        List<String> grantedPermissions = new ArrayList<>();
        for (String manifestPermission : manifestPermissions) {
            try {
                if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) {
                        grantedPermissions.add(manifestPermission);
                    }
                } else {
                    PermissionInfo permissionInfo = getPermissionInfo(context, manifestPermission);
                    int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
                    if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(context, manifestPermission) == PackageManager.PERMISSION_GRANTED) {
                        grantedPermissions.add(manifestPermission);
                    }
                }
            } catch (PackageManager.NameNotFoundException e) {
                // Skip this permission and continue.
                Log.w(TAG, "Unable to identify permission " + manifestPermission, e);
            }
        }

        return grantedPermissions.toArray(new String[0]);
    }

    /**
     * Check if the given permission is in the AndroidManifest.xml file.
     * @param context the caller context for this method.
     * @param permission the permission to look for in the manifest file.
     * @return "true" if the permission is in the manifest file of the activity, "false" otherwise.
     */
    public static boolean hasManifestPermission(Context context, String permission) {
        try {
            for (String p : getManifestPermissions(context)) {
                if (permission.equals(p))
                    return true;
            }
        } catch (PackageManager.NameNotFoundException ignored) {
        }

        return false;
    }

    /**
     * Returns the permissions defined in the AndroidManifest.xml file.
     * @param context the caller context for this method.
     * @return manifest permissions list
     * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
     */
    public static List<String> getManifestPermissions(Context context) throws PackageManager.NameNotFoundException {
        PackageManager packageManager = context.getPackageManager();
        PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
        if (packageInfo.requestedPermissions == null)
            return Collections.emptyList();
        return Arrays.asList(packageInfo.requestedPermissions);
    }

    /**
     * Returns the information of the desired permission.
     * @param context the caller context for this method.
     * @param permission the name of the permission.
     * @return permission info object
     * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
     */
    private static PermissionInfo getPermissionInfo(Context context, String permission) throws PackageManager.NameNotFoundException {
        PackageManager packageManager = context.getPackageManager();
        return packageManager.getPermissionInfo(permission, 0);
    }
}